
use pest::Parser;
use pest_derive::Parser;

#[derive(Parser)]
#[grammar = "lexer.pest"]
pub struct ExpressionParser;

#[derive(Debug, PartialEq, Clone)]
pub enum Token {
    // 关键字
    Const,
    Int,
    Void,
    If,
    Else,
    While,
    Break,
    Continue,
    Return,
    
    // 运算符
    Plus,
    Minus,
    Mul,
    Div,
    Mod,
    Assign,
    Eq,
    Neq,
    Lt,
    Gt,
    Le,
    Ge,
    Not,
    And,
    Or,
    
    // 界符
    LParen,
    RParen,
    LBrace,
    RBrace,
    LBrackt,
    RBrackt,
    Comma,
    Semicolon,

    // 标识符和常量
    Ident(String),
    Integer(i64),
}

#[derive(Debug)]
pub enum LexerError {
    ParseError(String, usize), // (错误信息, 行号)
}

pub fn tokenize(input: &str) -> Result<Vec<(Token, String, usize)>, Vec<LexerError>> {
    // let pairs = ExpressionParser::parse(Rule::program, input)
    //     .map_err(|e| LexerError::ParseError(e.to_string(), 1))?;
    let pairs = ExpressionParser::parse(Rule::program, input);
    let pairs = match pairs {
        Ok(pairs) => pairs,
        Err(e) => {
            let error_msg = format!("{}", e);
            let line_no = input.lines().count();
            return Err(vec![LexerError::ParseError(error_msg, line_no)]);
        }
    };

    let mut tokens = Vec::new();
    let mut errors: Vec<LexerError> = Vec::new();

    for pair in pairs {
        for inner_pair in pair.into_inner() {
            let rule = inner_pair.as_rule();
            let text = inner_pair.as_str();
            let span = inner_pair.as_span();
            
            // 计算行号
            let line_no = input[..span.start()].chars().filter(|&c| c == '\n').count() + 1;
            
            // 只处理TOKEN规则，忽略其他
            if rule == Rule::TOKEN {
                for token_pair in inner_pair.into_inner() {
                    let token_rule = token_pair.as_rule();
                    let token_text = token_pair.as_str();
                    
                    let (token, output_text) = match token_rule {
                        Rule::CONST => (Token::Const, "const".to_string()),
                        Rule::INT => (Token::Int, "int".to_string()),
                        Rule::VOID => (Token::Void, "void".to_string()),
                        Rule::IF => (Token::If, "if".to_string()),
                        Rule::ELSE => (Token::Else, "else".to_string()),
                        Rule::WHILE => (Token::While, "while".to_string()),
                        Rule::BREAK => (Token::Break, "break".to_string()),
                        Rule::CONTINUE => (Token::Continue, "continue".to_string()),
                        Rule::RETURN => (Token::Return, "return".to_string()),
                        Rule::PLUS => (Token::Plus, "+".to_string()),
                        Rule::MINUS => (Token::Minus, "-".to_string()),
                        Rule::MUL => (Token::Mul, "*".to_string()),
                        Rule::DIV => (Token::Div, "/".to_string()),
                        Rule::MOD => (Token::Mod, "%".to_string()),
                        Rule::ASSIGN => (Token::Assign, "=".to_string()),
                        Rule::EQ => (Token::Eq, "==".to_string()),
                        Rule::NEQ => (Token::Neq, "!=".to_string()),
                        Rule::LT => (Token::Lt, "<".to_string()),
                        Rule::GT => (Token::Gt, ">".to_string()),
                        Rule::LE => (Token::Le, "<=".to_string()),
                        Rule::GE => (Token::Ge, ">=".to_string()),
                        Rule::NOT => (Token::Not, "!".to_string()),
                        Rule::AND => (Token::And, "&&".to_string()),
                        Rule::OR => (Token::Or, "||".to_string()),
                        Rule::L_PAREN => (Token::LParen, "(".to_string()),
                        Rule::R_PAREN => (Token::RParen, ")".to_string()),
                        Rule::L_BRACE => (Token::LBrace, "{".to_string()),
                        Rule::R_BRACE => (Token::RBrace, "}".to_string()),
                        Rule::L_BRACKT => (Token::LBrackt, "[".to_string()),
                        Rule::R_BRACKT => (Token::RBrackt, "]".to_string()),
                        Rule::COMMA => (Token::Comma, ",".to_string()),
                        Rule::SEMICOLON => (Token::Semicolon, ";".to_string()),
                        Rule::IDENT => (Token::Ident(token_text.to_string()), token_text.to_string()),
                        Rule::INTEGER_CONST => {
                            // let value = parse_integer_const(token_text)
                            //     .map_err(|e| LexerError::ParseError(e, line_no))?;
                            // (Token::Integer(value), value.to_string())
                            match parse_integer_const(token_text) {
                                Ok(value) => (Token::Integer(value), value.to_string()),
                                Err(e) => {
                                    errors.push(LexerError::ParseError(e, line_no));
                                    continue;
                                }
                            }
                        },
                        _ => continue,
                    };
                    
                    tokens.push((token, output_text, line_no));

                }
            }

            if rule == Rule::UNKNOWN {
                let error_msg = format!("Mysterious character \"{}\".", text.chars().next().unwrap());
                errors.push(LexerError::ParseError(error_msg, line_no));
            }
        }
    }
    
    if !errors.is_empty() {
        Err(errors)
    } else {
        Ok(tokens)
    } 
    
}


fn parse_integer_const(s: &str) -> Result<i64, String> {
    if s.starts_with("0x") || s.starts_with("0X") {
        // 十六进制
        i64::from_str_radix(&s[2..], 16)
            .map_err(|_| format!("Invalid hexadecimal number: {}", s))
    } else if s.starts_with('0') && s.len() > 1 {
        // 八进制
        i64::from_str_radix(&s[1..], 8)
            .map_err(|_| format!("Invalid octal number: {}", s))
    } else {
        // 十进制
        s.parse::<i64>()
            .map_err(|_| format!("Invalid decimal number: {}", s))
    }
}
